/*
 * Created on Sun Jun 10 2018 by Ronnie Ren (zhaosong.ren) from Accenture
 *
 * Copyright (c) 2018 DingXin information & technology limited company.
 */

import { HttpService, HTTPMethods, APIPath } from "http-service";
import { Injectable } from "@angular/core";
import { API } from "../../models/api-path-extension";
import { Observable } from "rxjs/Observable";
import { CurdService } from "../../models/services/curd.service";

import { StorageDto } from "../../models/dto/storage.dto";
import { CustomStorage } from "../../models/transform/custom-storage";

import { SyncUtilService } from "./sync.util.service";
import { ObjectDto } from "../sqliteDB/object.dto";

import { StorageService } from "../../models/services/storage.service";

import { JSONMapping } from "../../models/json-mapping";
import { InspectionAttachmentsDto } from "../../models/dto/inspection/inspection-attachments.dto";
import { InspectionManualsDto } from "../../models/dto/inspection/inspection-manuals.dto";
import { InspectionTasksDto } from "../../models/dto/inspection/inspection-tasks.dto";
import { InspectionRecordsDto } from "../../models/dto/inspection/inspection-records.dto";
import { InspectionOperationsTicketsDto } from "../../models/dto/inspection/inspection-operation-tickets.dto";
import { subscribeOn } from "rxjs/operator/subscribeOn";
import { InspectionTasks } from "../../models/transform/inspection/inspection-tasks";
import { InspectionRecords } from "../../models/transform/inspection/inspection-records";
import { InspectionOperationsTickets } from "../../models/transform/inspection/inspection-operation-tickets";
import { InspectionManuals } from "../../models/transform/inspection/inspection-manuals";
import { WorkTicket } from "../../models/transform/inspection/work-ticket";
import { WorkTicketDto } from "../../models/dto/inspection/work-ticket.dto";
import { InspectionSysUser } from "../../models/transform/inspection/inspection-sysUser";
import { InspectionSysUserDto } from "../../models/dto/inspection/inspection-sysUser.dto";
import { SecureStorageService } from "../secure-storage.services";
import { InspectionType } from "../../models/transform/inspection/inspection-type";
import { InspectionTypeDto } from "../../models/dto/inspection/inspection-type.dto";
import { InspectionCompany } from "../../models/transform/inspection/inspection-company";
import { InspectionCompanyDto } from "../../models/dto/inspection/inspection-company.dto";
import { InspectionPersonnel } from "../../models/transform/inspection/inspection-personnel";
import { InspectionPersonnelDto } from "../../models/dto/inspection/inspection-personnel.dto";
import {Attachment} from "../../models/transform/attachment";
import {WorkOrderDto} from "../../models/dto/work-orders.dto";
import {LastFetchingDate} from "../../models/lastFetchingDate";
import { UtilsService } from "../utils.services";
import {RoleStatus} from "../../models/role.status";

declare var window: {
  localStorage: {
    getItem: any,
  }
};

@Injectable()
export class InspectionSyncService {
  pageSize: number = 50;
  roleList = [];
  customStorageData: Array<object>;
  constructor(
    private httpService: HttpService,
    private curdService: CurdService,
    private syncUtilService: SyncUtilService,
    private utilsService: UtilsService,
    private customeStorageService: StorageService,

  ) {
    let role =  window.localStorage.getItem('role');
    if (role) {
      this.roleList = role.split(',');
    }
  }

  fetchAppData() {
    this.customeStorageService.fetchValueByKey("lastFetchingDate")
      .subscribe((val) => {
      let lastFetchingDate = null;
      if (val && val.length > 0) {
        lastFetchingDate = new Date(parseInt(val, 10));
      }
    });
  }

  startFetch(): Observable<any> {
    // let lastFetchingDate = dateStr;
    let now = new Date().getTime();
    this.utilsService.showLoading();
    return Observable.combineLatest(
      this.syncUtilService.getApiLastFetchingPage(LastFetchingDate.inspectionPage)
        .flatMap((lastFetchingPage) => {
          return this.remoteFetchAllInspection( lastFetchingPage);
        }),
      // this.remoteFetchAllInspection(1)
      //   .do(() => {
      //     this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.getInspectionList, value: now.toString() });
      //   }),
      this.remoteInspectionType()
        .map(() => {
          this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.czlx, value: now.toString() });
        }),
      this.remoteInspectionCompany()
        .map(() => {
          this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.fldw, value: now.toString() });
        }),
      this.remoteInspectionPerson()
        .map(() => {
          this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.flr, value: now.toString() });
        })
    )
    .flatMap(() => this.startFetchUser(RoleStatus.producer))
      .map(() => {
        this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.inspectionSysUser, value: now.toString() });
      })
    .do((val) => {
      // TODO use the date returned from server.
      let lastFetchingDateStorage = new CustomStorage({ key: "lastFetchingDate", value: now.toString() });

      this.syncUtilService.saveData(
        [this.syncUtilService.toObjectDto(lastFetchingDateStorage, StorageDto)],
        StorageDto,
        "key").subscribe();
    });

  }

  startFetchUser(role) {
    // let lastFetchingDate = dateStr;
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.inspectionSysUser)
      .flatMap((lastFetchingDate) => {
        let date = new Date();
        let shouldUpdateDate = new Date(`${date.getFullYear()}-${date.getMonth() + 1}-01 00:00`);
        if (lastFetchingDate && lastFetchingDate > shouldUpdateDate) {
          return Observable.of([]);
        } else {
          return Observable.combineLatest(
            this.remoteFetchInspectionSysUserData(lastFetchingDate, role),
          );
        }
      });
    // .do((val) => {
    //   // TODO use the date returned from server.
    //   console.log(val);
    //   let lastFetchingDateStorage = new CustomStorage({ key: "lastFetchingDate", value: now.toString() });
    //
    //   this.syncUtilService.saveData(
    //     [this.syncUtilService.toObjectDto(lastFetchingDateStorage, StorageDto)],
    //     StorageDto,
    //     "key").subscribe();
    // });
    // .subscribe(() => {
    //   // increasemental pulling data successfully
    //
    // }, (error: object) => {
    //   console.log("increasemental pulling data failed" + error);
    // });

  }

  remoteFetchAllInspection(
    page: number,
    pageSize: number = this.pageSize):
    Observable<boolean> {

    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.getInspectionList)
      .flatMap((lastFetchingDate) => {
        return this.syncUtilService.remoteFetchWithArray(lastFetchingDate, new API().path(
          { path: "getInspectionList" }), InspectionTasks, page, pageSize)
          .do((inspectionTasks: Array<InspectionTasks>) => {
            inspectionTasks.forEach((inspectionTask) => {
              if (inspectionTask.record) {
                inspectionTask.record.taskId = inspectionTask.id;
              }
            });
          })
          .flatMap((inspectionTasks: Array<InspectionTasks>) => {
            let ob: Observable<boolean>;
            if (inspectionTasks.length >= pageSize) {
              // fetch next page & save data
              ob = this.remoteFetchAllInspection(page + 1, pageSize);
            }
            else {
              // finish & exit
              ob = Observable.create((observaber) => {
                this.syncUtilService.saveApiLastFetchingDate({ key: LastFetchingDate.getInspectionList, value: new Date().getTime().toString() });
                observaber.next();
                observaber.complete();
              })
                .flatMap(() => {
                  return this.syncUtilService.updateStoragePage({ key: LastFetchingDate.inspectionPage, value: "1" });
                })
                .map(() => Observable.of(true));
            }
            return this.saveAllInspection(inspectionTasks)
              .flatMap(() => {
                return this.syncUtilService.updateStoragePage({ key: LastFetchingDate.inspectionPage, value: page.toString() });
              })
              .flatMap(() => ob);
          });
      });
  }

  saveRecord(inspectionRecords: Array<InspectionRecords>): Observable<any> {
    let RecordsDtos = inspectionRecords.map((records: InspectionRecords) => {
      let RecordsDto = this.syncUtilService.toObjectDto(records, InspectionRecordsDto);
      // id is a calculated property, we have to call it to get it created first time.
      RecordsDto.taskId = records.taskId;
      return RecordsDto;
    });
    return this.syncUtilService.saveData(RecordsDtos, InspectionRecordsDto, "taskId");
      // .flatMap(() => {
      //   let attachmentArray = [];
      //   inspectionRecords.map((inspectionRecord) => {
      //     attachmentArray = attachmentArray.concat(
      //       Attachment.useAttachmentIdsAndThumbnailIdsGetAttachments(inspectionRecord.pictureAttachmentIds, null, true));
      //   });
      //   console.log(inspectionRecords, "inspectionRecords开始下载图片");
      //   attachmentArray = this.syncUtilService.downImageAndGetLocalUrl(attachmentArray);
      //   if (attachmentArray.length > 0) {
      //     return Observable.combineLatest(attachmentArray)
      //       .flatMap((attachments: Array<Attachment>) => {
      //         return this.syncUtilService.saveAttachment(attachments);
      //       });
      //   } else {
      //     return Observable.of(false);
      //   }
      // });
  }

  saveOperationsTickets(inspectionOperationsTickets: Array<InspectionOperationsTickets>): Observable<any> {
    let OperationsTicketsDtos = inspectionOperationsTickets.map((operationsTickets: InspectionOperationsTickets) => {
      let OperationsTicketsDto = this.syncUtilService.toObjectDto(operationsTickets, InspectionOperationsTicketsDto);
      // id is a calculated property, we have to call it to get it created first time.
      OperationsTicketsDto.taskId = operationsTickets.taskId;
      return OperationsTicketsDto;
    });
    return this.syncUtilService.saveData(OperationsTicketsDtos, InspectionOperationsTicketsDto, "id,taskId");
      // .map(() => Observable.of([]));
      // .flatMap(() => {
      //   let attachmentArray = [];
      //   inspectionOperationsTickets.map((inspectionOperationsTicket) => {
      //     attachmentArray = attachmentArray.concat(
      //       Attachment.useAttachmentIdsAndThumbnailIdsGetAttachments(inspectionOperationsTicket.attachmentIds, null, true));
      //   });
      //   console.log(inspectionOperationsTickets, "inspectionOperationsTickets开始下载图片");
      //   attachmentArray = this.syncUtilService.downImageAndGetLocalUrl(attachmentArray);
      //   if (attachmentArray.length > 0) {
      //     return Observable.combineLatest(attachmentArray)
      //       .flatMap((attachments: Array<Attachment>) => {
      //         return this.syncUtilService.saveAttachment(attachments);
      //       });
      //   } else {
      //     return Observable.of(false);
      //   }
      // });
  }

  saveManuals(inspectionManuals: Array<InspectionManuals>): Observable<any> {
    let inspectionManualsDtos = inspectionManuals.map((manuals: InspectionManuals) => {
      let inspectionManualsDto = this.syncUtilService.toObjectDto(manuals, InspectionManualsDto);
      // id is a calculated property, we have to call it to get it created first time.
      inspectionManualsDto.taskId = manuals.taskId;
      return inspectionManualsDto;
    });
    return this.syncUtilService.saveData(inspectionManualsDtos, InspectionManualsDto, "id");
  }

  saveWorkTicket(workTickets: Array<WorkTicket>): Observable<any> {
    let WorkTicketDtos = workTickets.map((workTicket: WorkTicket) => {
      let workTicketDto = this.syncUtilService.toObjectDto(workTicket, WorkTicketDto);
      // id is a calculated property, we have to call it to get it created first time.
      workTicketDto.taskId = workTicket.taskId;
      workTicketDto.content = JSON.stringify(workTicket.content).replace(/"/g, "'");
      console.log('工作票', workTicketDto);
      return workTicketDto;
    });
    return this.syncUtilService.saveData(WorkTicketDtos, WorkTicketDto, "id");
  }

  private remoteFetchInspectionSysUserData(lastFetchingDate?: Date, role?: string) {
    let api = new API().path({ path: "inspectionSysUser", method: HTTPMethods.Get });
    api.baseURL = api.baseURL + "/" + role;
    return this.remoteFetchData(
      lastFetchingDate,
      api,
      "account",
      InspectionSysUser,
      InspectionSysUserDto);
  }

  private remoteInspectionType() {
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.czlx)
      .flatMap((lastFetchingDate) => {
        let api = new API().path({path: "insTypeCompanyPerson", method: HTTPMethods.Get});
        api.baseURL = api.baseURL + "/CZLX";
        return this.remoteFetchData(
          lastFetchingDate,
          api,
          "id",
          InspectionType,
          InspectionTypeDto);
      });
  }

  private remoteInspectionCompany() {
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.fldw)
      .flatMap((lastFetchingDate) => {
        let api = new API().path({path: "insTypeCompanyPerson", method: HTTPMethods.Get});
        api.baseURL = api.baseURL + "/FLDW";
        return this.remoteFetchData(
          lastFetchingDate,
          api,
          "id",
          InspectionCompany,
          InspectionCompanyDto);
      });
  }

  private remoteInspectionPerson() {
    return this.syncUtilService.getApiLastFetchingDate(LastFetchingDate.flr)
      .flatMap((lastFetchingDate) => {
        let api = new API().path({path: "insTypeCompanyPerson", method: HTTPMethods.Get});
        api.baseURL = api.baseURL + "/FLR";
        return this.remoteFetchData(
          lastFetchingDate,
          api,
          "id",
          InspectionPersonnel,
          InspectionPersonnelDto);
      });
  }

  private saveAllInspection(inspectionTasks: Array<InspectionTasks>, isPaidBills: boolean = false): Observable<any> {
    // 检修列表
    let inspectionTasksDtos = inspectionTasks.map((inspectionTask) => this.syncUtilService.toObjectDto(inspectionTask, InspectionTasksDto));
    return this.syncUtilService.saveData(inspectionTasksDtos, InspectionTasksDto, "id")
      .flatMap(() => {
        // 勘察记录
        let records = inspectionTasks.filter((inspectionTask) => inspectionTask.record).map((inspectionTask) => inspectionTask.record);
        if (!records || records.length < 1) {
          return Observable.of([]);
        } else {
          return Observable.combineLatest( this.saveRecord(records));
        }
      })
      .flatMap( () => {
        // 操作票
        let operationsTickets = inspectionTasks.filter((inspectionTask) => inspectionTask.operationsTicket)
        .map((inspectionTask) => inspectionTask.operationsTicket);
        if (!operationsTickets || operationsTickets.length < 1) {
          return Observable.of([]);
        } else {
          return Observable.combineLatest( this.saveOperationsTickets(operationsTickets));
        }
      })
      .flatMap( () => {
        // 作业指导书
        let manualsObs = inspectionTasks.filter((inspectionTask) => inspectionTask.manuals).map((inspectionTask) => {
          return this.saveManuals(inspectionTask.manuals);
        });
        if (!manualsObs || manualsObs.length < 1) {
          return Observable.of([]);
        } else {
          return Observable.combineLatest(manualsObs);
        }

      })
      .flatMap(() => {
        // 工作票
        let theWorkTickets = inspectionTasks.filter((inspectionTask) => inspectionTask.workTicket)
        .map((inspectionTask) => inspectionTask.workTicket);
        if (!theWorkTickets || theWorkTickets.length < 1) {
          return Observable.of([]);
        } else {
          return Observable.combineLatest( this.saveWorkTicket(theWorkTickets));
        }
      });
  }

  private remoteFetchData<T extends ObjectDto, M extends JSONMapping>(
    lastFetchingDate: Date,
    apiPath: APIPath,
    existCheckingPropertyKey: string,
    modelConstructorFunc: { new(json: object): M },
    dtoConstructorFunc: { new(): T }): Observable<boolean> {

    return this.syncUtilService.remoteFetchWithArray(
      lastFetchingDate,
      apiPath,
      modelConstructorFunc)
      .map((models) => {
        console.log(models, modelConstructorFunc, "debuger 44444444444444444444");
        return models.map((model) => {
          console.log(JSON.stringify(models), "debuger 4444");
          return this.syncUtilService.toObjectDto(model, dtoConstructorFunc);
        });
      })
      .flatMap((dtos: Array<T>) => {
        return this.syncUtilService.saveData(dtos, dtoConstructorFunc, existCheckingPropertyKey);
      })
      .map(() => true);
  }
}


